package com.huangfu.learning.javabase.java.generic;


/**
 * Created with IntelliJ IDEA.
 * Author: huangfu.daohui
 * Date: 2018/7/10
 * Time: 14:39
 * Version: 1.0
 * Description:泛型类
 * 通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类，如：List、Set、Map。
 * 泛型的类型参数只能是类类型，不能是简单类型。
 * 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的，编译时会出错。
 * 常用用法：定义一个base类 <T,PK>,T为VO实体，PK为主键，实现类传入具体的VO类型和PK类型
 * eg：class BaseService<T, PK>
 * XXXServiceImpl extends BaseService<User,Integer>
 **/
////此处 T可以随便写为任意标识，常见的如T、E、K、V等形式的参数常用于表示泛型
////在实例化泛型类时，必须指定T的具体类型
public class GenericClass<T> {

    private T key;

    // 泛型构造器形参也是T ，T的类型由外部（调用方）指定
    public GenericClass(T key) {
        this.key = key;
    }

    //泛型方法返回值为T，由外部指定（该方法不是泛型方法，是泛型的成员方法，下面的才是泛型方法，因为有声明<T>）
    public T getKey() {
        return key;
    }

    //带泛型形参的泛型方法，要在返回类型钱定义泛型类型
    public <E> T getKeyWithParam(E e) {
        System.out.println("E e:" + e);
        return key;
    }


    // 泛型通配符
    public void showKeyValue1(GenericClass<Number> obj) {
        System.out.println("泛型测试 key value is " + obj.getKey());
    }

    // 泛型通配符  用这个通配符，就可以兼容Integer Number等所有类型
    //此处’？’是类型实参，而不是类型形参 。重要说三遍！此处’？’是类型实参，而不是类型形参 ！ 此处’？’是类型实参，而不是类型形参 ！
    public void showKeyValue2(GenericClass<?> obj) {
        System.out.println("泛型测试 key value is " + obj.getKey());

    }

    public static void main(String[] args) {
        //泛型的类型参数只能是类类型（包括自定义类），不能是简单类型
        //传入的实参类型需与泛型的类型参数类型相同，即为Integer.
        GenericClass<Integer> GenericInteger = new GenericClass<Integer>(123);
        //传入的实参类型需与泛型的类型参数类型相同，即为String
        GenericClass<String> GenericString = new GenericClass<String>("huangfu");

        System.out.println("GenericInteger:" + GenericInteger.getKey());
        System.out.println("GenericString:" + GenericString.getKey());

        System.out.println("-------------------------------------");

        GenericClass generic = new GenericClass("111111");//String
        GenericClass generic1 = new GenericClass(4444); //Integer
        GenericClass generic2 = new GenericClass(55.55); //Float
        GenericClass generic3 = new GenericClass(false); //boolean


        System.out.println("generic:" + generic.getKey());//111111
        System.out.println("generic1:" + generic1.getKey());//4444
        System.out.println("generic2:" + generic2.getKey());//55.55
        System.out.println("generic3:" + generic3.getKey());//false //generic3.getKey().getClass().getTypeName()  Boolean

        System.out.println("-------------------------------");
        System.out.println("getKeyWithParam:" + generic.getKeyWithParam(123));

        System.out.println("===============================");
        GenericClass<Integer> gInteger = new GenericClass<Integer>(123);
        GenericClass<Number> gNumber = new GenericClass<Number>(456);
        GenericClass<String> gString = new GenericClass<String>("huang");

        gNumber.showKeyValue1(gNumber);

//         showKeyValue这个方法编译器会为我们报错：Generic<java.lang.Integer>
//         cannot be applied to Generic<java.lang.Number>
//        gInteger.showKeyValue1(gInteger);


        System.out.println("===========通配符测试===========");
        //用通配符就可以正常编译
        gNumber.showKeyValue2(gNumber);
        gInteger.showKeyValue2(gInteger);
        gString.showKeyValue2(gString);
    }
}